package com.fast.common.pager;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PageMateInterceptor implements Interceptor {

	private static Logger logger = LoggerFactory.getLogger(PageMateInterceptor.class);

	public Object intercept(final Invocation invocation) throws Throwable {
		long start = System.currentTimeMillis();
		if (invocation.getTarget() instanceof StatementHandler) {
			StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
			MetaObject statehandler = SystemMetaObject.forObject(statementHandler);
			MappedStatement mappedStatement = (MappedStatement) statehandler.getValue("delegate.mappedStatement");

			Object rowBounds = statehandler.getValue("delegate.rowBounds");
			Pageable pageable = null;
			if (rowBounds instanceof Pageable) {
				pageable = (Pageable) rowBounds;
			}

			/**
			 * 如果方法中没有调用Pageable对象，则传入的值仍旧为RowBounds对象
			 */
			if (pageable == null || pageable.getPageNo() <= 0  ) {
				long end = System.currentTimeMillis();
				logger.info("执行时间==>{} ms", end - start);
				return invocation.proceed();
			}
			/**
			 * 如果对象中存在Pageable对象，说明该对象准备调用分页查询
			 */
			// 获取分页对象
			BoundSql boundSql = (BoundSql) statehandler.getValue("delegate.boundSql");
			String sql = boundSql.getSql();
			Connection connection = (Connection) invocation.getArgs()[0];
			int total = getCount(sql, connection, mappedStatement, boundSql);
			pageable.setTotal(total);
			if (total > 0) {
				String pageSql = builderPageSql(sql, pageable, mappedStatement);
				statehandler.setValue("delegate.boundSql.sql", pageSql);
				long end = System.currentTimeMillis();
				logger.info("执行时间：{} ms ", end - start);
			} else {
				logger.info("总记录数小于等于0，未返回数据");
			}
		}
		return invocation.proceed();
	}

	/**
	 * 构造分页sql语句
	 * 
	 * @param sql
	 * @param page
	 * @param mappedStatement
	 * @return
	 */
	private String builderPageSql(String sql, Pageable page, MappedStatement mappedStatement) {
		StringBuilder str = new StringBuilder();
		str.append(sql);
		List<Order> orders = page.getOrders();
		if (orders != null && orders.size() > 0) {
			StringBuilder orderstr = new StringBuilder();
			orderstr.append(" order by  ");
			for (Order ord : orders) {
				String expr = ord.toString();
				orderstr.append(expr);
				orderstr.append("  ,  ");
			}
			int lstIndex = orderstr.lastIndexOf(",");
			if (lstIndex > 0) {
				String order = orderstr.substring(0, lstIndex);
				str.append(order);
			}
		}
		str.append(" limit ");
		str.append(page.getStart());
		str.append(" , ");
		str.append(page.getEnd());

		return str.toString();
	}

	/**
	 * 获取分页总记录数
	 * 
	 * @param sql
	 * @param connection
	 * @param mappedStatement
	 * @param boundSql
	 * @param pageNo
	 * @return
	 */
	private int getCount(String sql, Connection conn, MappedStatement mapperState, BoundSql boundSql) {
		String countSql = "select count(0) from (" + sql + ") tmp";
		PreparedStatement countStmt = null;
		ResultSet rs = null;
		try {
			countStmt = conn.prepareStatement(countSql);
			BoundSql countBS = new BoundSql(mapperState.getConfiguration(), countSql, boundSql.getParameterMappings(),
					boundSql.getParameterObject());
			setParameters(countStmt, mapperState, countBS, boundSql.getParameterObject());
			rs = countStmt.executeQuery();
			int totalCount = 0;
			if (rs != null && rs.next()) {
				totalCount = rs.getInt(1);
			}
			return totalCount;
		} catch (SQLException e) {
			logger.error("Ignore this exception", e);
		} finally {
			try {
				if (rs != null) {
					rs.close();
					rs = null;
				}
			} catch (SQLException e) {
				logger.error("Ignore this exception", e);
			}
			try {
				if (countStmt != null) {
					countStmt.close();
					countStmt = null;
				}
			} catch (SQLException e) {
				logger.error("Ignore this exception", e);
			}
		}
		return 0;
	}

	/**
	 * 代入参数值
	 * 
	 * @param ps
	 * @param mappedStatement
	 * @param boundSql
	 * @param parameterObject
	 * @throws SQLException
	 */
	private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
			Object parameterObject) throws SQLException {
		ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
		parameterHandler.setParameters(ps);
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
	}

}
